#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <sys/epoll.h>
#include "socket.hpp"
using namespace socket_ns;

class EpollServer
{
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket(_port);
        _epfd = ::epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll create error!\n");
            exit(1);
        }
        LOG(INFO, "epoll create success! epfd:%d\n", _epfd);
    }
    ~EpollServer()
    {
        if (_epfd >= 0)
        {
            close(_epfd);
        }
        _listensock->Close();
    }
    void Init()
    {
        // 将listensock添加关心
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock->Sockfd();
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(ERROR, "listensock add error\n");
        }
        LOG(ERROR, "listensock add success!\n");
    }
    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listensock->Accepter(&addr);
        if (sockfd < 0)
        {
            LOG(ERROR, "listensock accept error!\n");
            return;
        }
        LOG(INFO, "get a new link,client info %s:%d!\n", addr.IP(), addr.Port());

        // 将sockfd添加到关心
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sockfd;
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (n < 0)
        {
            LOG(ERROR, "sockfd add error\n");
        }
        LOG(INFO, "epoll_ctl success, add new sockfd : %d\n", sockfd);
    }
    void HanderIO(int fd)
    {
        char buffer[1024];
        int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl;
            std::string respstr = "HTTP/1.0 200 OK\r\n";
            std::string content="<html><body><h1>hello</h1></body></html>";
            respstr += "Content-Type: text/html\r\n";
            respstr += "Content-Length: " + std::to_string(content.size()) + "\r\n";
            respstr += "\r\n";
            respstr+=content;
            ::send(fd,respstr.c_str(),respstr.size(),0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit!\n");
            // 1.将fd在epoll移出
            int n = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (n < 0)
            {
                LOG(ERROR, "IO_fd DEL error,fd:%d\n", fd);
                exit(2);
            }
            // 2.关闭文件描述符
            ::close(fd);
        }
        else
        {
            LOG(ERROR, "recv error!\n");
            // 1.将fd在epoll移出
            int n = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (n < 0)
            {
                LOG(ERROR, "IO_fd DEL error,fd:%d\n", fd);
                exit(2);
            }
            // 2.关闭文件描述符
            ::close(fd);
        }
    }
    void HanderEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = _recvs[i].data.fd;
            if (_recvs[i].events & EPOLLIN)
            {
                if (fd == _listensock->Sockfd())
                {
                    Accepter();
                }
                else
                {
                    HanderIO(fd);
                }
            }
        }
    }

    void Loop()
    {
        int timeout = 3000;
        while (true)
        {
            int n = ::epoll_wait(_epfd, _recvs, num, timeout);
            switch (n)
            {
            case 0:
                // 超时了
                LOG(INFO, "epoll_wait timeout!\n");
                break;
            case -1:
                LOG(INFO, "epoll_wait error!\n");
                sleep(3);
                break;
            default:
                // 有事件发生
                HanderEvent(n);
                break;
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<TcpSocket> _listensock;
    int _epfd;
    struct epoll_event _recvs[num];
};
